home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
C/C++ Interactive Reference Guide
/
C-C++ Interactive Reference Guide.iso
/
c_ref
/
csource2
/
sclib_2
/
2_6
/
v6n6044a.txt
< prev
next >
Wrap
Text File
|
1995-11-01
|
12KB
|
341 lines
\NLETTER\6.6\TRIANGLE.C/*
Copyright (C) Magna Carta Software, 1988. All Rights Reserved.
Scroll and pan a graphics image on the screen in video mode 0X10 (640x350
16-color graphics). Note: This program assumes the presence of an EGA
driving an Enhanced Color Display or a VGA. Furthermore, that this adapter
is active. Simple invocataion syntax:
TURBO C: tcc sgraph
MSC v5.0+: cl /DMSC sgraph.c
POWER C v1.1+ pc /e sgraph
WATCOM C v6.0+ wcc sgraph -ms
wlink FILE sgraph LIB ?:\watcomc\lib\clibs, ?:\watcomc\lib\maths
OPTION Map, caseexact, stack=2048
First version: 5/7/88. Last revised: 6/1/88.
*/
#include <conio.h>
#include <dos.h>
#include <stdio.h>
typedef unsigned char BYTE;
/* COMPILER SPECIFIC DETAILS */
#if defined(MSC) || defined(__WATCOMC__)
#define inportb(port) inp(port)
#define outportb(port,value) outp((port),(value))
#define outport(port,value) outpw((port),(value))
#endif
/* MANIFEST CONSTANTS */
#define FALSE 0
#define TRUE !FALSE
#define LOGICAL_WIDTH 132 /* logical screen width in bytes */
#define VIDEO 0X10 /* BIOS interrupt 0X10 -- video */
#define YELLOW 14 /* the color for the graphics image */
/* EGA and VGA register values */
#define START_ADDRESS_HIGH 0X0C /* Address of start address h reg. of CRTC */
#define START_ADDRESS_LOW 0X0D /* Address of start address l reg. of CRTC */
#define AC_INDEX 0X3C0 /* Attribute Controller Index Register */
#define AC_HPP 0X13 | 0X20 /* Horizontal Pel Panning Register */
/* Note: ORed with 20H to preserve */
/* bit 5 */
#define OFFSET_REG 0X013
/* GLOBAL VARIABLES */
unsigned right_edge, left_edge; /* TRUE if we are at the edge of the screen */
int vpel, hpel;
int mono = FALSE; /* EGA monochrome */
struct video_descriptor {
BYTE far *base; /* starting address of the video buffer */
unsigned isr; /* EGA/VGA input status register address */
unsigned crtc; /* CRT Controller Register address */
};
struct screen_descriptor {
unsigned rows;
unsigned cols;
unsigned logical_width; /* screen logical width in words. max: 0X100 */
unsigned start_addr; /* screen start address. max: 0X4000 */
};
struct video_descriptor video;
struct screen_descriptor screen;
/* FUNCTION PROTOTYPES */
int fill_triag(int color, int v[][2]);
void scanline(unsigned x0, unsigned x1, unsigned y, int color);
unsigned smooth_scroll(int count, unsigned speed);
unsigned smooth_pan(int count, unsigned speed);
unsigned set_logical_screen_width(unsigned l_width);
void set_start_addr(unsigned start_addr);
void set_pel_pan(int hpel);
void main(void)
{
union REGS regs;
/* top ----- left corner - right corner */
static int v1[][2] = {{100, 25}, {50, 125}, {150, 125}};
/* ESTABLISH THE SYSTEM STATUS */
/* initialize the base address of the video buffer */
video.base = (BYTE far *) 0XA0000000L;
screen.start_addr = 0; /* initialize the start address */
regs.x.ax = 0X0010; /* set video mode to 10 hex. */
int86(0X10, ®s, ®s);
/* initialize the logical screen width in bytes */
screen.logical_width = LOGICAL_WIDTH;
screen.cols = screen.logical_width;
video.isr = 0X03da; /* address of input status register */
video.crtc = video.isr - 6; /* address of CRT controller register */
/* SET THE LOGICAL SCREEN WIDTH FOR PANNING AND THE START ADDRESS */
set_logical_screen_width(screen.logical_width);
set_start_addr(screen.start_addr);
/* WE MUST DO A DUMMY READ OF THE INPUT STATUS REGISTER TO INITIALIZE
THE TOGGLE OF THE ATTRIBUTE CONTROLLER (see text of article ) */
inportb(video.isr);
fill_triag(YELLOW, v1); /* draw the graphic image on the screen */
/* ALL THE SCROLLING-PANNING ACTION IS HANDLED HERE */
do {
smooth_scroll(-100,2);
smooth_pan(-300,1);
smooth_scroll(100,2);
smooth_pan(300,1);
} while (!kbhit());
/* A KEY WAS HIT -- RETURN TO 80 COLUMN COLOR TEXT MODE AND EXIT */
getch();
regs.x.ax = 0X0003;
int86(0X10, ®s, ®s);
}
/* Fill an isosceles triangle with one side contiguous with a scan line and
an apex at the top.
*/
int fill_triag(int color, int v[][2])
{
int y, start, end;
double left_slope, right_slope, x0, x1, x2, y0, y1, y2;
x0 = v[0][0];
x1 = v[1][0];
x2 = v[2][0];
y0 = v[0][1];
y1 = v[1][1];
y2 = v[2][1];
left_slope = (x1 - x0)/(y1 - y0); /* Compute slopes */
right_slope = (x2 - x0)/(y2 - y0); /* Compute slopes */
for (y = y0; y <= y1; y++) { /* Loop over raster lines */
start = x0 + (y - y0)*left_slope;
end = x0 + (y - y0)*right_slope;
scanline(start,end,y,color); /* Fill next section */
}
return (0);
}
void scanline(unsigned start, unsigned stop, unsigned raster, int color)
{
#define GRAPHICSC 0X03CE
#define SEQUENCER 0X03C4
#define BIT_MASK 8
unsigned i, mask;
BYTE oddbits, byte, far *s_offset;
int scan_len;
scan_len = stop - start; /* length -1 in pixels */
/* --- Load SET/RESET registers with current color */
outport(GRAPHICSC, color << 8); /* move color into reset register */
outport(GRAPHICSC, 0X0F01); /* enable use of reset register */
outport(SEQUENCER, 0X0F02); /* enable all planes for write */
/* ----------- COMPUTE ADDRESS AND MASK FOR FIRST PIXEL ------------- */
s_offset = video.base + screen.logical_width*raster + (start >> 3);
if ( (oddbits = start & 7) != 0) {
mask = 0X00FF >> oddbits;
scan_len += oddbits;
scan_len -= 8;
if (scan_len < 0) {
scan_len = (~scan_len) + 1;
mask = mask >> (BYTE) scan_len;
mask = mask << (BYTE) scan_len;
scan_len = 0;
}
outport(GRAPHICSC, (mask << 8) | BIT_MASK); /* unmask bit mask */
byte = *s_offset; /* latch data */
*s_offset++ = byte; /* write data */
}
/* ---------------- WRITE COMPLETE BYTES -------------------- */
if (scan_len >= 8) {
outport(GRAPHICSC, (0XFFFF << 8) | BIT_MASK); /* unmask bit mask */
for (i=0; i < (scan_len >> 3); i++) *s_offset++ = byte;
}
/* --- COMPUTE ADDRESS AND MASK FOR LAST PIXEL ------------- */
if ( (oddbits = scan_len & 7) != 0) {
mask = (0XFF >> oddbits) ^ 0XFF;
outport(GRAPHICSC, (mask << 8) | BIT_MASK);
byte = *s_offset; /* latch data */
*s_offset++ = byte; /* write data */
}
/* RESTORE BIT MASK AND SET/RESET REGISTERS */
outport(GRAPHICSC, (0XFFFF << 8) | BIT_MASK);
outport(GRAPHICSC, 0X0001);
}
/* SMOOTH_SCROLL scrolls the EGA video buffer the number of scan lines
indicated in "count" at a speed of "speed" scan lines per vertical retrace.
*/
unsigned smooth_scroll(int count, unsigned speed)
{
unsigned i, max_count;
max_count = (count < 0) ? -count : count;
/* GET THE START ADDRESS OF THE SCREEN BUFFER */
outportb(video.crtc, START_ADDRESS_HIGH); /* High byte */
screen.start_addr = inportb(video.crtc+1) << 8;
outportb(video.crtc, START_ADDRESS_LOW); /* Low byte */
screen.start_addr |= inportb(video.crtc+1);
/* COUNT > 0 => SCROLL SCREEN IMAGE UPWARDS. */
for(i=0;i < max_count; i+=speed) {
screen.start_addr = (count > 0) ?
screen.start_addr + screen.logical_width*speed :
screen.start_addr - screen.logical_width*speed;
/* wait for the end of a vertical retrace */
while (inportb(video.isr) & 8);
/* wait for the next vertical retrace */
while (!(inportb(video.isr) & 8));
set_start_addr(screen.start_addr);
}
return (screen.start_addr);
}
/* SMOOTH_PAN
This function invokes smooth panning on the EGA/VGA. The function calculates
the number of scan lines per row. The speed variable adjusts the speed in
pixels per vertical retrace and takes values in the range 1-8.
*/
unsigned smooth_pan(int count, unsigned speed)
{
unsigned i;
/* count greater than zero (move viewport to the right) */
if (count>0 && !right_edge) for(i=0;i<count;) {
/* if we have scrolled a full character, reset start address */
if (hpel>=8) {
screen.start_addr++;
left_edge = FALSE;
if (!(screen.start_addr % (screen.logical_width))) right_edge = TRUE;
if (!right_edge) hpel %= 8; /* reset the pel counter */
else {
hpel = 0;
i = count - 1;
}
set_start_addr(screen.start_addr);
}
for(;hpel < 8 && i < count;i+=speed, hpel+=speed) set_pel_pan(hpel);
}
/* count less than zero (move viewport to the left) */
else if (count<0) {
if (hpel>7) hpel = hpel - 8;
for(i=0;i<(-count) ;) {
/* if we have scrolled a full character, reset start address */
if (hpel<0 && !left_edge) {
screen.start_addr--;
right_edge = FALSE;
if (!(screen.start_addr % (screen.logical_width))) left_edge = TRUE;
hpel = 8 + hpel;
set_start_addr(screen.start_addr);
}
else if (hpel<0 && left_edge) i = (-count);
for(;hpel >= 0 && i < (-count);i+=speed, hpel-=speed) set_pel_pan(hpel);
}
}
return (screen.start_addr);
}
/* SET_LOGICAL_SCREEN_WIDTH defines an EGA/VGA screen width for smooth panning
and sets the the global variable "screen.cols." screen.cols must be restored
when smooth panning is finished or subsequent screen writes will address the
wrong screen locations.
l_width is the width of the logical screen in bytes. Max 512.
*/
unsigned set_logical_screen_width(unsigned l_width)
{
l_width = (l_width > 512) ? 512 : l_width;
/* set screen_cols */
screen.cols = l_width;
/* set logical screen width */
l_width >>= 1; /* convert to words */
outport(video.crtc, (l_width << 8) | OFFSET_REG);
return (l_width << 1);
}
/* SET_START_ADDRESS -- Sets the start address of the screen. I.e. the */
/* address that occupies the top left of the screen. */
void set_start_addr(unsigned start_addr)
{
/* address the start address high register */
outport(video.crtc, (start_addr & 0XFF00) | START_ADDRESS_HIGH);
/* address the start address low register */
outport(video.crtc, (start_addr << 8) | START_ADDRESS_LOW);
}
/* SET_PEL_PAN -- Sets the horizontal pel panning register to the value */
/* specified in "hpel". Called by smooth_pan() */
void set_pel_pan(int hpel)
{
/* first wait for end of vertical retrace */
while (inportb(video.isr) & 8);
/* wait for next vertical retrace */
while (!(inportb(video.isr) & 8));
/* address the horizontal pel paning register */
outportb(AC_INDEX, AC_HPP);
outportb(AC_INDEX, hpel);
}